﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Collections.ObjectModel;

namespace PistonWheel
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private DispatcherTimer _timer;
        private double timerTickedCount;
        private Piston piston;
        private Rod rod;
        private Wheel wheel;

        ObservableCollection<Wheel> wheelData;
        ObservableCollection<Rod> rodData;
        ObservableCollection<Piston> pistonData;

        HamiltonianSolver solver;

        public MainWindow()
        {
            createPiston();
            _timer = new DispatcherTimer();
            _timer.Interval = new TimeSpan( 0, 0, 0, 0, 50 );
            _timer.Tick += new EventHandler( _timer_Tick );
            InitializeComponent();

            this.viewport.Children.Add( this.wheel );
            this.viewport.Children.Add( this.rod );
            this.viewport.Children.Add( this.piston );
            this.viewport.UpdateLayout();
            this.wheelData = new ObservableCollection<Wheel>();
            this.rodData = new ObservableCollection<Rod>();
            this.pistonData = new ObservableCollection<Piston>();
        }

        void createPiston()
        {
            Brush backBrushMaterial;
            Brush pistonFrontBrushMaterial;
            Brush rodFrontBrushMaterial;
            try
            {
                backBrushMaterial = (Brush)this.FindResource( "backMaterialColour" );
                pistonFrontBrushMaterial = (Brush)this.FindResource( "pistonColour" );
                rodFrontBrushMaterial = (Brush)this.FindResource( "rodColour" );
            }
            catch (ResourceReferenceKeyNotFoundException e)
            {
                MessageBox.Show( e.Message );
                return;
            }

            this.wheel = new Wheel( backBrushMaterial, pistonFrontBrushMaterial );
            this.wheel.Fold1 = 0.1;
            this.wheel.Fold2 = 0.1;
            this.wheel.Radius1 = this.wheel.Radius2 = 1.0;
            this.wheel.Point1 = new System.Windows.Media.Media3D.Point3D( 0.0, -2.0, -0.2 );
            this.wheel.Point2 = new System.Windows.Media.Media3D.Point3D( 0.0, -2.0, 0.2 );

            this.rod = new Rod( backBrushMaterial, rodFrontBrushMaterial, this.wheel );
            this.rod.Fold1 = 0.1;
            this.rod.Fold2 = 0.15;
            this.rod.Radius1 = this.rod.Radius2 = 0.1;
            this.rod.Point1 = new System.Windows.Media.Media3D.Point3D( -1.0, -2.0, 0.2 );
            this.rod.Point2 = new System.Windows.Media.Media3D.Point3D( 0.0, 1.0, 0.2 );

            this.piston = new Piston( this.wheel, this.rod, backBrushMaterial, pistonFrontBrushMaterial );
            this.piston.Point1 = new System.Windows.Media.Media3D.Point3D( 0.0, 1.0, 0.0 );
            this.piston.Point2 = new System.Windows.Media.Media3D.Point3D( 0.0, this.piston.Point1.Y + this.piston.Height, 0.0 );
            this.piston.Radius1 = this.piston.Radius2 = 1.0;
            this.piston.Fold1 = 0.15;
            this.piston.Fold2 = 0.1;
        }

        void _timer_Tick(object sender, EventArgs e)
        {
            //solve equations
            this.solver.solve(ref this.timerTickedCount );
            double yPos = this.solver.getPistonPosition( this.wheel.Angle, this.timerTickedCount );

            this.piston.Point1 = new System.Windows.Media.Media3D.Point3D( this.piston.Point1.X, yPos, this.piston.Point1.Z );
            this.piston.Point2 = new System.Windows.Media.Media3D.Point3D( this.piston.Point1.X, this.piston.Point1.Y + this.piston.Height, this.piston.Point1.Z );

            double yr = this.wheel.Radius * Math.Cos( this.wheel.Angle ) + this.wheel.Middle.Y;
            double xr = this.wheel.Radius * Math.Sin( this.wheel.Angle ) + this.wheel.Middle.X;

            this.rod.Point2 = new System.Windows.Media.Media3D.Point3D( this.rod.Point2.X, yPos, this.rod.Point2.Z );
            this.rod.Point1 = new System.Windows.Media.Media3D.Point3D( xr, yr, this.rod.Point1.Z );
            double a = Math.Asin( this.wheel.Radius * Math.Sin( this.wheel.Angle ) / this.rod.L );
            this.rod.AngularVelocity = a - this.rod.Angle;
            this.rod.Angle = a;

            this.wheel.Time = this.timerTickedCount;
            this.rod.Time = this.timerTickedCount;
            this.piston.Time = this.timerTickedCount;

            this.wheelData.Add( (Wheel)this.wheel.Clone() );
            this.rodData.Add( (Rod)this.rod.Clone() );
            this.pistonData.Add( (Piston)this.piston.Clone() );

            this.currPistonSpeed.Text = String.Format( "{0:F3}", this.piston.Speed );
            this.currWheelSpeed.Text = String.Format( "{0:F3}", this.wheel.AngularVelocity );
           // this.timerTickedCount++;
        }

        private void playBtn_Click(object sender, RoutedEventArgs e)
        {
            this.playBtn.SetValue( IsEnabledProperty, false );
            this.angVelocityTextBoxSlider.SetValue( IsEnabledProperty, false );
            this.epsilonTextBoxSlider.SetValue( IsEnabledProperty, false );
            this.pauseBtn.SetValue( IsEnabledProperty, true );
            this.rod.LengthStdDev = this.epsilonTextBoxSlider.Value;
            this.solver = new HamiltonianSolver( 0.001, this.piston, this.wheel, this.rod );
            this._timer.Start();
        }

        private void pauseBtn_Click(object sender, RoutedEventArgs e)
        {
            this._timer.Stop();
            this.pauseBtn.SetValue( IsEnabledProperty, false );
            this.playBtn.SetValue( IsEnabledProperty, true );
            this.angVelocityTextBoxSlider.SetValue( IsEnabledProperty, true );
            this.epsilonTextBoxSlider.SetValue( IsEnabledProperty, true );
        }

        private void chartsBtn_Click(object sender, RoutedEventArgs e)
        {
            Charts chartsWindow = new Charts();
            chartsWindow.WheelData = this.wheelData;
            chartsWindow.RodData = this.rodData;
            chartsWindow.PistonData = this.pistonData;
            chartsWindow.AddRodDataLineSeries();
            chartsWindow.AddWheelSpeedLineSeries();
            chartsWindow.AddPistonDataLineSeries();
            chartsWindow.Show();
        }

        private void pistonMassTextBoxSlider_OnSplitterValueChanged(object sender, SpringMass.SplitterEventsArgs sEventsArgs)
        {
            this.piston.Mass = this.pistonMassTextBoxSlider.Value;
        }

        private void rodMassTextBoxSlider_OnSplitterValueChanged(object sender, SpringMass.SplitterEventsArgs sEventsArgs)
        {
            this.rod.Mass = this.rodMassTextBoxSlider.Value;
        }

        private void angVelocityTextBoxSlider_OnSplitterValueChanged(object sender, SpringMass.SplitterEventsArgs sEventsArgs)
        {
            this.wheel.AngularVelocity = this.angVelocityTextBoxSlider.Value;
        }

        private void rTextBoxSlider_OnSplitterValueChanged(object sender, SpringMass.SplitterEventsArgs sEventsArgs)
        {
            this.wheel.Radius = this.rTextBoxSlider.Value;
        }

        private void lTextBoxSlider_OnSplitterValueChanged(object sender, SpringMass.SplitterEventsArgs sEventsArgs)
        {
            this.rod.L = this.lTextBoxSlider.Value;
        }

        private void animationSpeedSlider_OnSplitterValueChanged(object sender, SpringMass.SplitterEventsArgs sEventsArgs)
        {
            this._timer.Interval = new TimeSpan( 0, 0, 0, 0, 50 * (int)(this.animationSpeedSlider.MaxValue - this.animationSpeedSlider.Value) );
        }

        private void clearBtn_Click(object sender, RoutedEventArgs e)
        {
            this.wheelData.Clear();
            this.rodData.Clear();
            this.pistonData.Clear();
        }

        private void Viewbox_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            double aspect = e.NewSize.Width / e.NewSize.Height;
            camera.Position = new System.Windows.Media.Media3D.Point3D( camera.Position.X, camera.Position.Y, 7.0 + Math.Pow( aspect * 2.2, 2 ) );
            double width = this.renderCol.ActualWidth;
            double height = this.row1.ActualHeight;
            this.viewport.SetValue( WidthProperty, width );
            this.viewport.SetValue( HeightProperty, height );
            this.viewport.UpdateLayout();
            this.renderCanvas.SetValue( WidthProperty, width );
            this.renderCanvas.SetValue( HeightProperty, height );
            this.renderCanvas.UpdateLayout();
            this.viewbox.UpdateLayout();
        }
    }
}
